/*
    SUSAN® - Sole of Unix Save ANything

   Copyright (C) 2013-2013 Hechzh Property

   
*/
#include <QDebug>
#include <QTimer>

#include "mainwindow.h"
#include "monitoritemthread.h"
#include "tray-monitor.h"
#include "monitoritem.h"
#include "tray_conf.h"
#include "lib/parse_conf.h"

#include <cassert>

MonitorItemThread* MonitorItemThread::monitorItemThreadSingleton;
bool MonitorItemThread::already_destroyed = false;

MonitorItemThread::MonitorItemThread(QObject* parent)
    : QThread(parent)
    , monitor(NULL)
    , refreshTimer(new QTimer(this))
    , isRefreshing(false)
{
  threadId = currentThreadId();

  refreshTimer->setObjectName("RefreshTimer");
  QMetaObject::connectSlotsByName(this);
}

MonitorItemThread::~MonitorItemThread() { return; }

MonitorItemThread* MonitorItemThread::instance()
{
  // improve that the MonitorItemThread is created
  // and deleted only once during program execution
  Q_ASSERT(!already_destroyed);

  if (!monitorItemThreadSingleton) {
    monitorItemThreadSingleton = new MonitorItemThread;
  }
  return monitorItemThreadSingleton;
}

void MonitorItemThread::destruct()
{
  if (monitorItemThreadSingleton) {
    monitorItemThreadSingleton->exit(0);
    monitorItemThreadSingleton->wait(20000);
    delete monitorItemThreadSingleton;
    monitorItemThreadSingleton = NULL;
    already_destroyed = true;
  }
}

Qt::HANDLE MonitorItemThread::getThreadId() { return threadId; }

void MonitorItemThread::run()
{
  /* all this must be run in the same
   * context of the MonitorItemThread  */

  if (monitor) { refreshTimer->start(monitor->RefreshInterval * 1000); }

  exec();

  while (items.count()) {
    MonitorItem* item = items.first();
    item->disconnect();
    delete item;
    items.removeFirst();
  }

  TermMsg();  // this cannot be called twice, however
}

QStringList MonitorItemThread::createRes(const cl_opts& cl)
{
  QStringList tabRefs;

  LockRes(my_config);

  int monitorItems = 0;
  MonitorResource* monitorRes;
  foreach_res (monitorRes, R_MONITOR) {
    monitorItems++;
  }

  if (monitorItems != 1) {
    const std::string& configfile = my_config->get_base_config_path();
    Emsg2(M_ERROR_TERM, 0,
          _("Error: %d Monitor resources defined in %s. "
            "You must define one and only one Monitor resource.\n"),
          monitorItems, configfile.c_str());
  }

  monitor = dynamic_cast<MonitorResource*>(
      my_config->GetNextRes(R_MONITOR, (SusanResource*)NULL));
  assert(monitor);

  int nitems = 0;

  DirectorResource* dird;
  foreach_res (dird, R_DIRECTOR) {
    MonitorItem* item = new MonitorItem;
    item->setType(R_DIRECTOR);
    item->setResource(dird);
    item->setConnectTimeout(monitor->DIRConnectTimeout);
    if (!cl.do_connection_test_only_) {
      item->connectToMainWindow(MainWindow::instance());
    }
    tabRefs.append(item->get_name());
    items.append(item);
    nitems++;
  }

  ClientResource* filed;
  foreach_res (filed, R_CLIENT) {
    MonitorItem* item = new MonitorItem;
    item->setType(R_CLIENT);
    item->setResource(filed);
    item->setConnectTimeout(monitor->FDConnectTimeout);
    if (!cl.do_connection_test_only_) {
      item->connectToMainWindow(MainWindow::instance());
    }
    tabRefs.append(item->get_name());
    items.append(item);
    nitems++;
  }

  StorageResource* stored;
  foreach_res (stored, R_STORAGE) {
    MonitorItem* item = new MonitorItem;
    item->setType(R_STORAGE);
    item->setResource(stored);
    item->setConnectTimeout(monitor->SDConnectTimeout);
    if (!cl.do_connection_test_only_) {
      item->connectToMainWindow(MainWindow::instance());
    }
    tabRefs.append(item->get_name());
    items.append(item);
    nitems++;
  }

  UnlockRes(my_config);

  if (nitems == 0) {
    Emsg1(M_ERROR_TERM, 0,
          "No Client, Storage or Director resource defined in %s\n"
          "Without that I don't know how to get status from the File, "
          "Storage or Director Daemon :-(\n",
          cl.configfile_);
  }

  // check the refresh intervals for reasonable values
  int interval = monitor->RefreshInterval;
  if ((interval < 1) || (interval > 600)) {
    Emsg2(M_ERROR_TERM, 0,
          "Invalid refresh interval defined in %s\n"
          "This value must be greater or equal to 1 second and less or "
          "equal to 10 minutes (read value: %d).\n",
          cl.configfile_, interval);
  }

  return tabRefs;
}

void MonitorItemThread::onRefreshItems()
{
  if (!isRefreshing) {
    isRefreshing = true;
    for (int i = 0; i < items.count(); i++) { items[i]->GetStatus(); }
    emit refreshItemsReady();
    isRefreshing = false;
  }
}

bool MonitorItemThread::doConnectionTest()
{
  int failed = 0;
  for (int i = 0; i < items.count(); i++) {
    bool success = items[i]->doconnect();
    if (success) {
      items[i]->disconnect();
    } else {
      ++failed;
    }
  }
  return failed ? false : true;
}

void MonitorItemThread::on_RefreshTimer_timeout() { onRefreshItems(); }

MonitorResource* MonitorItemThread::getMonitor() const { return monitor; }

MonitorItem* MonitorItemThread::getDirector() const
{
  // search for the first occurrence of a director

  int count = items.count();
  for (int i = 0; i < count; i++) {
    if (items[i]->type() == R_DIRECTOR) { return items[i]; }
  }
  return NULL;
}


/************* Testing ***************/

void MonitorItemThread::dotest()
{
  const char* cmd;
  int count = items.count();

  for (int i = 0; i < count; i++) {
    switch (items[i]->type()) {
      case R_DIRECTOR:
        cmd = ".jobs type=B";
        items[i]->docmd(cmd);
        break;
      default:
        break;
    }
  }
}
